/**
@C++ Standard: 11
@Copyright:Copyright (c) 2019 Harold Hu. All rights reserved.
@Author: Created by Harold Hu with CLion on 2019/1/12 10:47.
@File Name: BENCHMARK_MAIN.cpp
@Description: For Benchmark Framework.
*/
#include "HaroldDebug.h"

#if TEST_TYPE == TEST_BENCHMARK

#include <benchmark/benchmark.h>
#include <vector>
#include <set>
#include <sstream>
#include <cstring>

static int64_t gDest = 1;

static void combine1(benchmark::State& state)
{
	for (auto _ : state)
	{
		for (int i = 0; i < state.range(1); ++i)
		{
			*(reinterpret_cast<int64_t*>(state.range(0))) = *(reinterpret_cast<int64_t*>(state.range(0))) + i;
		}
	}
}

//BENCHMARK(combine1)->Args({(int64_t) &gDest, 10000});

static void combine2(benchmark::State& state)
{
	for (auto _ : state)
	{
		int64_t tmp = *((int64_t*) state.range(0));
		for (int i = 0; i < state.range(1); ++i)
		{
			tmp = tmp + i;
		}
		*(reinterpret_cast<int64_t*>(state.range(0))) = tmp;
	}
}

//BENCHMARK(combine2)->Args({(int64_t) &gDest, 10000});

static void combine3(benchmark::State& state)
{
	for (auto _ : state)
	{
		auto range0 = reinterpret_cast<int64_t*>(state.range(0));
		auto range1 = static_cast<int>(state.range(1));
		for (int i = 0; i < range1; ++i)
		{
			*range0 = *range0 + i;
		}
	}
}

//BENCHMARK(combine3)->Args({(int64_t) &gDest, 10000});

static void combine4(benchmark::State& state)
{
	for (auto _ : state)
	{
		auto range0 = reinterpret_cast<int64_t*>(state.range(0));
		auto range1 = static_cast<int>(state.range(1));
		int64_t tmp = *((int64_t*) state.range(0));
		for (int i = 0; i < range1; ++i)
		{
			tmp = tmp + i;
		}
		*range0 = tmp;
	}
}

//BENCHMARK(combine4)->Args({(int64_t) &gDest, 10000});

string uint2str(unsigned int num)
{
	std::ostringstream oss;
	oss << num;
	return oss.str();
}

static void BM_uint2str(benchmark::State& state)
{
	unsigned int num = 1234;
	while (state.KeepRunning())
	{
		(void) uint2str(num);
	}
}
// Register the function as a benchmark
//BENCHMARK(BM_uint2str)->Unit(benchmark::kNanosecond);
//BENCHMARK(BM_uint2str)->Unit(benchmark::kMillisecond);
//BENCHMARK(BM_uint2str)->Unit(benchmark::kMicrosecond);

void BM_spin_empty(benchmark::State& state)
{
	for (auto _ : state)
	{
		for (int x = 0; x < state.range(0); ++x)
		{
			benchmark::DoNotOptimize(x);
		}
	}
}

BENCHMARK(BM_spin_empty)->Arg(2)->ComputeStatistics("max", [](const std::vector<double>& v) -> double
{
	cout << "hello" << endl;
	return *(std::max_element(std::begin(v), std::end(v)));
});

void demo()
{
	string str = "hello world";
	str.size();
}


static void BM_demo(benchmark::State& state)
{
	for (auto _ : state)
	{
		demo();
	}
	cout << state.range(0) << endl;
	cout << state.range(1) << endl;
	cout << state.range(2) << endl;
}
// Register the function as a benchmark
//BENCHMARK(BM_demo)->Iterations(10000)->Args({1, 2, 3}); //指定BM_demo函数中，for循环的迭代次数

static void BM_StringCreation(benchmark::State& state)
{
	for (auto _ : state)
	{
		std::string empty_string;
	}
}
// Register the function as a benchmark
//BENCHMARK(BM_StringCreation);

// Define another benchmark
static void BM_StringCopy(benchmark::State& state)
{
	std::string x = "hello";
	for (auto _ : state)
	{
		std::string copy(x);
	}
}

//BENCHMARK(BM_StringCopy);

static void BM_memcpy(benchmark::State& state)
{
	char* src = new char[state.range(0)];
	char* dst = new char[state.range(0)];
	memset(src, 'x', (size_t) state.range(0));
	for (auto _ : state)
	{
		memcpy(dst, src, (size_t) state.range(0));
	}
	state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
	delete[] src;
	delete[] dst;
}

//BENCHMARK(BM_memcpy)->Arg(8)->Arg(64);

static void BM_StringCompare(benchmark::State& state)
{
	std::string s1(static_cast<unsigned long>(state.range(0)), '-');
	std::string s2(static_cast<unsigned long>(state.range(0)), '-');
	for (auto _ : state)
	{
		benchmark::DoNotOptimize(s1.compare(s2));
	}
	state.SetComplexityN(state.range(0));
}

BENCHMARK(BM_StringCompare)->RangeMultiplier(2)->Range(1 << 10, 1 << 11)->Complexity(benchmark::oN);
//BENCHMARK(BM_StringCompare)->RangeMultiplier(2)->Range(1 << 10, 1 << 11)->Complexity();
BENCHMARK(BM_StringCompare)->RangeMultiplier(2)->Range(1 << 10, 1 << 11)->Complexity([](int64_t n) -> double
																					 {
																						 return n;
																					 });

static void BM_SetInsert(benchmark::State& state)
{
	std::set<int> data;
	for (auto _ : state)
	{
		state.PauseTiming();
		/* pause timing */
		state.ResumeTiming();
		for (int j = 0; j < state.range(1); ++j)
		{
			data.insert(j);
		}
	}
}

//BENCHMARK(BM_SetInsert)->Args({1 << 10, 128})->Args({2 << 10, 128})->Args({4 << 10, 128})->Args({8 << 10, 128})->Args(
//		{1 << 10, 512})->Args({2 << 10, 512})->Args({4 << 10, 512})->Args({8 << 10, 512});
//BENCHMARK(BM_SetInsert)->Ranges({{1<<10, 8<<10}, {128, 512}});

static void CustomArguments(benchmark::internal::Benchmark* b)
{
	for (int i = 0; i <= 2; ++i)
	{
		for (int j = 32; j <= 32 * 64; j *= 8)
		{
			b->Args({i, j});
		}
	}
}

//BENCHMARK(BM_SetInsert)->Apply(CustomArguments);

template<class Q>
void BM_Sequential(benchmark::State& state)
{
	Q q;
	typename Q::value_type v;
	for (auto _ : state)
	{
		for (auto i = state.range(0); i--;)
		{
			q.push_back(v);
		}
		for (auto e = state.range(0); e--;)
		{
			q.emplace_back(v);
		}
	}
	// actually messages, not bytes:
	state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * state.range(0));
}

//BENCHMARK_TEMPLATE(BM_Sequential, std::vector<int>)->Range(1 << 0, 1 << 4);


BENCHMARK_MAIN();

#endif